﻿#pragma once

#include "Misc/EngineEditorMacros.h"
#include "Object/Object.h"
#include "Math/Vec2.h"
#include "MaterialGraph/UndoRedo/MaterialEditOperation.h"
#include "MaterialGraph/Node/MaterialGraphNodeModel.h"
#include "MaterialGraphModel.m.h"

class RMaterialGraphNodeModel;
class RMaterialGraphLinkModel;
class RMaterialGraphPinModel;
class RMaterial;
class RMaterialEditOperation;

//the editor object of the material 
//will be saved into the material asset as EditorObject
RCLASS(ExtraHeader= [ "MaterialGraph/Node/MaterialGraphNodeModel.h", "MaterialGraph/Link/MaterialGraphLinkModel.h" ])
class RMaterialGraphModel : public RObject
{
    ROBJECT

public:

    bool CanUndo() const;
    bool CanRedo() const;
    void Undo();
    void Redo();

    int64_t GetNodeNum() const;
    TSharedObjectPtr<RMaterialGraphNodeModel> GetNode(int64_t Index) const;
    void AddNode(TSubclassOf<RMaterialGraphNodeModel> NodeClass,const SVec2& NodePosition);
    void AddNode(TSharedObjectPtr<RMaterialGraphNodeModel> Node);
    TDelegate<void,int64_t> OnNodeAdded;
    void RemoveNode(TSharedObjectPtr<RMaterialGraphNodeModel> Node);
    TDelegate<void,int64_t> OnNodeRemoved;

    int64_t GetLinkNum() const;
    TSharedObjectPtr<RMaterialGraphLinkModel> GetLink(int64_t Index) const;
    void AddLink(TSharedObjectPtr<RMaterialGraphLinkModel> Link);
    TDelegate<void,int64_t> OnLinkAdded;
    void RemoveLink(TSharedObjectPtr<RMaterialGraphLinkModel> Link);
    TDelegate<void,int64_t> OnLinkRemoved;

    TSharedObjectPtr<RMaterialGraphLinkModel> CreateAndAddLink(
        TSharedObjectPtr<RMaterialGraphNodeModel> StartNode
        , int64_t InOuputPinIndex
        , TSharedObjectPtr<RMaterialGraphNodeModel> EndNode
        , int64_t InInputPinIndex);

    TSharedObjectPtr<RMaterialGraphLinkModel> CreateAndAddLink(
        TSharedObjectPtr<RMaterialGraphPinModel> StartPin
        , TSharedObjectPtr<RMaterialGraphPinModel> EndPin);



    
    void AddOperation(TSharedObjectPtr<RMaterialEditOperation> Operation);
    
    //update the material
    void RegenerateMaterial(TSharedObjectPtr<RMaterial> InMaterial);

    bool HasUnsavedData() const;
    void Save(TSharedObjectPtr<RMaterial> InMaterial);

    void SetSelectedNode(TSharedObjectPtr<RMaterialGraphNodeModel> Node);
    TDelegate<void,TSharedObjectPtr<RMaterialGraphNodeModel>> OnSelectedNodeChanged;
    void RemoveSelectedNode();
    TSharedObjectPtr<RMaterialGraphNodeModel> GetSelectedNode() const;

    void SetCurrentOutputNode(TSharedObjectPtr<RMaterialGraphNodeModel> Node);
    TDelegate<void,TSharedObjectPtr<RMaterialGraphNodeModel>> OnCurrentOutputNodeChanged;
    TSharedObjectPtr<RMaterialGraphNodeModel> GetCurrentOutputNode() const;
protected:
    RFIELD()
    TVector<TSharedObjectPtr<RMaterialGraphNodeModel>> Nodes;

    RFIELD()
    TVector<TSharedObjectPtr<RMaterialGraphLinkModel>> Links;

    //当前选中的节点
    RFIELD()
    TSharedObjectPtr<RMaterialGraphNodeModel> SelectedNode;

    //当前材质的输出节点
    //材质图允许增加多个输出节点，但只有一个有效
    RFIELD()
    TSharedObjectPtr<RMaterialGraphNodeModel> CurrentOutputNode;


    TVector<TSharedObjectPtr<RMaterialEditOperation>> Operations;
    //the index of the last finished operation in the Operations
    int64_t LastOperationIndex=-1;

    TSharedObjectPtr<RMaterialEditOperation> OperationBeforeLastSave;

    void LowLevelInsertNode(TSharedObjectPtr<RMaterialGraphNodeModel> Node, int64_t Index);
    void LowLevelRemoveNode(int64_t Index);

    friend class RMaterialAddNodeOperation;
    friend class RMaterialRemoveNodeOperation;

    void LowLevelInsertLink(TSharedObjectPtr<RMaterialGraphLinkModel> Link, int64_t Index);
    void LowLevelRemoveLink(int64_t Index);

    void LowLevelSetOutputNode(TSharedObjectPtr<RMaterialGraphNodeModel> Node);

    friend class RMaterialAddLinkOperation;
    friend class RMaterialRemoveLinkOperation;
    friend class RMaterialSetOutputNodeOperation;
};


//the operation of adding a new node
RCLASS()
class RMaterialAddNodeOperation : public RMaterialEditOperation
{
    ROBJECT

public:

    RFIELD()
    TSharedObjectPtr<RMaterialGraphNodeModel> Node;

    SVec2 Position;

    int64_t NodeIndex;

    virtual void Undo(TSharedObjectPtr<RMaterialGraphModel> InGraph) override;
    virtual void Redo(TSharedObjectPtr<RMaterialGraphModel> InGraph) override;
};

//the operation of removing a node
RCLASS()
class RMaterialRemoveNodeOperation : public RMaterialEditOperation
{
    ROBJECT

public:
    int64_t NodeIndex;
    
    RFIELD()
    TSharedObjectPtr<RMaterialGraphNodeModel> Node;
    
    virtual void Undo(TSharedObjectPtr<RMaterialGraphModel> InGraph) override;
    virtual void Redo(TSharedObjectPtr<RMaterialGraphModel> InGraph) override;
};


//the operation of adding a new link
RCLASS()
class RMaterialAddLinkOperation : public RMaterialEditOperation
{
    ROBJECT

public:
    RFIELD()
    TSharedObjectPtr<RMaterialGraphLinkModel> AddedLink;
    
    int64_t LinkIndex;

    virtual void Undo(TSharedObjectPtr<RMaterialGraphModel> InGraph) override;
    virtual void Redo(TSharedObjectPtr<RMaterialGraphModel> InGraph) override;
};

//the operation of removing a link
RCLASS()
class RMaterialRemoveLinkOperation : public RMaterialEditOperation
{
    ROBJECT

public:
    RFIELD()
    TSharedObjectPtr<RMaterialGraphLinkModel> RemovedLink;
    
    int64_t LinkIndex;

    virtual void Undo(TSharedObjectPtr<RMaterialGraphModel> InGraph) override;
    virtual void Redo(TSharedObjectPtr<RMaterialGraphModel> InGraph) override;
};

RCLASS()
class RMaterialSetOutputNodeOperation : public RMaterialEditOperation
{
    ROBJECT

public:
    RFIELD()
    TSharedObjectPtr<RMaterialGraphNodeModel> OldOutputNode;

    RFIELD()
    TSharedObjectPtr<RMaterialGraphNodeModel> NewOutputNode;

    virtual void Undo(TSharedObjectPtr<RMaterialGraphModel> InGraph) override;
    virtual void Redo(TSharedObjectPtr<RMaterialGraphModel> InGraph) override;
};
